Default( "AchievementSendRate", 0.1 )

local ACH = {}
ACH.__index = ACH

local translate = {
	number = "Long";
	string = "String";
	boolean = "Bool";
}

if ( SERVER ) then
	function ACH:SendUpdate( ply )
	end

	function ACH:GetValue( ply, name, def )
		// If we got that value, get it.
		if ( self.Values[ ply ] != nil && self.Values[ ply ][ name ] != nil ) then
			return self.Values[ ply ][ name ]
		end
		// Otherwise return default.
		return def
	end
	function ACH:SetValue( ply, name, value )
		// Set the value for reference.
		self.Values[ ply ] = self.Values[ ply ] or {}
		self.Values[ ply ][ name ] = value
		// Check if we need to send it the the player.
		self.CheckSend[ply] = true
	end

	function GM:AchievementHook( name, args )
		for _, ach in pairs( self.Achievements ) do
			if ( ach[ name ] ) then
				local ok, err = pcall( ach[ name ], ach, unpack( args ) )
				if ( !ok ) then ErrorNoHalt( err ) end
			end
		end
	end
	
	function GM:CheckAchievementValues()
		// Loop through all the achievements.
		for _, ach in pairs( self.Achievements ) do
			// Loop through all players + value names needing checking.
			for ply, _ in pairs( ach.CheckSend ) do
				// Safety first!
				local ok, err = pcall( function()
					// See if we need to send an update.
					local send, store = ach:SendUpdate(ply, ach.LastSent[ply])
					if ( send != nil ) then
						// Send the data to the client.
						local success = self:SendAchievementValue(ply, ach, send)
						if success then
							// Store the value so the achievement can check against it later.
							ach.LastSent[ply] = store
							
							printd( "Sent update for ", ach.FileName, " to ", ply, ": ", tostring(send) )
							
							return
						else
							printd("Error sending ", ach.FileName, "unknown type", type(send))
						end
					end
					// It's been checked, and there's nothing you can do about it! So don't try anything stupid.
					ach.CheckSend[ply] = nil
				end )
				// We don't want to kill this function.
				if ( !ok ) then ErrorNoHalt( err ) end
			end
		end
	end
	timer.Create("Incursion.CheckAchievementValues", GM.AchievementSendRate, 0, function() GAMEMODE:CheckAchievementValues() end)
	
	function GM:SendAchievementValue(ply, ach, val)
		// Check we have a function for sending this type of value.
		local sendType = translate[ type( val ) ]
		local func = umsg[ sendType or "" ]
		if ( func ) then
			// Bitch got sent.
			umsg.Start( "Incursion.Achievement", ply )
				umsg.String( ach.FileName )
				umsg.String( type( val ) )
				func( val )
			umsg.End()
			
			return true
		end
		return false
	end
	
end
if ( CLIENT ) then
	local umsgMeta = FindMetaTable( "bf_read" )
	
	function ACH:GetValue( name, def )
		if ( !achievements || !self.FileName ) then return end
		return achievements.GetValue( self.FileName, name, def )
	end
	function ACH:SetValue( name, val )
		if ( !achievements || !self.FileName ) then return end
		achievements.SetValue( self.FileName, name, val )
	end
	
	function ACH:Register( fraction, text )
		if ( !achievements || !self.FileName ) then return end
		achievements.Register( self.Name, self.Description, self.Icon, fraction, text )
	end
	function ACH:Update( fraction, text )
		if ( !achievements || !self.FileName ) then return end
		achievements.Update( self.Name, fraction, text )
	end
	
	function ACH:ReceiveValue( value )
	end
	
	function GM:ReceiveAchievement( msg )
		local filename = msg:ReadString()
		local ach = self.Achievements[ filename ]
		if ( !ach ) then return end
		
		local valType = msg:ReadString()
		
		local readType = translate[ valType ]
		local meta = getmetatable( msg )
		if ( !meta ) then return end
		
		local func = meta[ "Read" .. readType ]
		if ( !func ) then return end

		local value = func( msg )
		
		local ok, err = pcall( function()
			ach:ReceiveValue( value )
		end )
		if ( !ok ) then ErrorNoHalt( err ) end
	end
	usermessage.Hook( "Incursion.Achievement", function( msg ) GAMEMODE:ReceiveAchievement( msg ) end )
end

GM.Achievements = {}
function GM:LoadAchievements()
	// Load 'er up.
	self.Achievements = {}
	local files, folders = file.Find( self.FolderName .. "/gamemode/shared/achievements/*.lua", "LUA" )
	for _, ach in pairs( files ) do
		printd( "Adding achievement " .. ach )
		
		ACHIEVEMENT = { CheckSend = {}, Values = {}, LastSent = {}, FileName = ach }
		setmetatable( ACHIEVEMENT, ACH )
		
		include( "achievements/" .. ach )
		
		if ( ACHIEVEMENT != nil ) then
			if ( SERVER ) then
				// Find and add any hooks.
				for k, v in pairs( ACHIEVEMENT ) do
					// We'll assume all serverside functions are hooks.
					if ( type( v ) == "function" ) then
						self:AddHook( k, "IncAchievements", function( gm, ... ) gm:AchievementHook( k, {...} ) end )
					end
				end
				
				AddCSLuaFile( "achievements/" .. ach )
			end
			
			self.Achievements[ ach ] = ACHIEVEMENT
		end
	end
	ACHIEVEMENT = nil
end
if ( SERVER ) then GM:LoadAchievements() end
if ( CLIENT ) then GM:AddHook( "AchievementsLoaded", "LoadAchievements" ) end